//===--- PILLocation.h - Location information for PIL nodes -----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_PIL_LOCATION_H
#define POLARPHP_PIL_LOCATION_H

#include "llvm/ADT/PointerUnion.h"
#include "polarphp/basic/SourceLoc.h"
#include "polarphp/basic/SourceMgr.h"
#include "polarphp/ast/TypeAlignments.h"

#include <cstddef>
#include <type_traits>

namespace polar {
class ReturnStmt;
class BraceStmt;
class AbstractClosureExpr;
class AbstractFunctionDecl;
class Decl;
class Expr;
class Stmt;
class Pattern;
class DeclContext;
using polar::SourceLoc;
using polar::SourceManager;
using polar::SourceRange;

/// This is a pointer to the AST node that a PIL instruction was
/// derived from. This may be null if AST information is unavailable or
/// stripped.
///
/// FIXME: This should eventually include inlining history, generics
/// instantiation info, etc (when we get to it).
///
class PILLocation {
private:
   template <class T, class Enable = void>
   struct base_type;

   template <class T>
   struct base_type<T,
      typename std::enable_if<std::is_base_of<Decl, T>::value>::type> {
      using type = Decl;
   };

   template <class T>
   struct base_type<T,
      typename std::enable_if<std::is_base_of<Expr, T>::value>::type> {
      using type = Expr;
   };

   template <class T>
   struct base_type<T,
      typename std::enable_if<std::is_base_of<Stmt, T>::value>::type> {
      using type = Stmt;
   };

   template <class T>
   struct base_type<T,
      typename std::enable_if<std::is_base_of<Pattern, T>::value>::type> {
      using type = Pattern;
   };

   using AstNodeTy = llvm::PointerUnion4<Stmt *, Expr *, Decl *, Pattern *>;

public:
   enum LocationKind : unsigned {
      RegularKind = 1,
      ReturnKind = 2,
      ImplicitReturnKind = 3,
      InlinedKind = 4,
      MandatoryInlinedKind = 5,
      CleanupKind = 6,
      ArtificialUnreachableKind = 7
   };

   enum StorageKind : unsigned {
      UnknownKind = 0,
      AstNodeKind = 1 << 3,
      PILFileKind = 1 << 4,
      DebugInfoKind = 1 << 3 | 1 << 4
   };

   struct DebugLoc {
      unsigned Line;
      unsigned Column;
      StringRef Filename;

      DebugLoc(unsigned Line = 0, unsigned Column = 0,
               StringRef Filename = StringRef())
         : Line(Line), Column(Column), Filename(Filename) {}

      inline bool operator==(const DebugLoc &R) const {
         return Line == R.Line && Column == R.Column &&
                Filename.equals(R.Filename);
      }
   };

protected:
   union UnderlyingLocation {
      UnderlyingLocation() : DebugInfoLoc({}) {}
      UnderlyingLocation(AstNodeTy N) : AstNode(N) {}
      UnderlyingLocation(SourceLoc L) : PILFileLoc(L) {}
      UnderlyingLocation(DebugLoc D)
         : DebugInfoLoc({D.Filename.data(), D.Line, D.Column}) {}
      struct AstNodeLoc {
         AstNodeLoc(AstNodeTy N) : Primary(N) {}
         /// Primary AST location, always used for diagnostics.
         AstNodeTy Primary;
         /// Sometimes the location for diagnostics needs to be
         /// different than the one used to emit the line table. If
         /// HasDebugLoc is set, this is used for the debug info.
         AstNodeTy ForDebugger;
      } AstNode;

      /// A location inside a textual .sil file.
      SourceLoc PILFileLoc;

      /// A deserialized source location.
      ///
      /// This represents \e most of the information in a PILLocation::DebugLoc,
      /// but for bit-packing purposes the length of the filename is stored in
      /// the PILLocation's ExtraStorageData instead of here.
      ///
      /// \sa PILLocation::getDebugInfoLoc
      struct CompactDebugLoc {
         const char *FilenameData;
         unsigned Line;
         unsigned Column;
      } DebugInfoLoc;
   } Loc;

   /// The kind of this PIL location.
   unsigned KindData;

   /// Extra data that's not in UnderlyingLocation to keep the size of
   /// PILLocation down.
   unsigned ExtraStorageData = 0;

   enum {
      LocationKindBits = 3,
      LocationKindMask = 7,

      StorageKindBits = 2,
      StorageKindMask = (1 << 3) | (1 << 4),
      SpecialFlagsMask = ~ (LocationKindMask | StorageKindMask),

      /// Used to mark this instruction as part of auto-generated
      /// code block.
         AutoGeneratedBit = 5,

      /// Used to redefine the default source location used to
      /// represent this PILLocation. For example, when the host instruction
      /// is known to correspond to the beginning or the end of the source
      /// range of the AstNode.
         PointsToStartBit = 6,
      PointsToEndBit = 7,

      /// Used to notify that this instruction belongs to the top-
      /// level (module) scope.
      ///
      /// FIXME: If Module becomes a Decl, this could be removed.
         IsInTopLevel = 8,

      /// Marks this instruction as belonging to the function prologue.
         IsInPrologue = 9
   };

   template <typename T>
   T *getNodeAs(AstNodeTy Node) const {
      using base = typename base_type<T>::type*;
      return llvm::dyn_cast_or_null<T>(Node.dyn_cast<base>());
   }

   template <typename T>
   bool isNode(AstNodeTy Node) const {
      assert(isAstNode());
      if (Loc.AstNode.Primary.is<typename base_type<T>::type*>())
         return llvm::isa<T>(Node.get<typename base_type<T>::type*>());
      return false;
   }

   template <typename T>
   T *castNodeTo(AstNodeTy Node) const {
      return llvm::cast<T>(Node.get<typename base_type<T>::type*>());
   }

   /// \defgroup PILLocation constructors.
   /// @{

   /// This constructor is used to support getAs operation.
   PILLocation() { assert(Loc.DebugInfoLoc.Line == 0); }
   PILLocation(LocationKind K, unsigned Flags = 0) : KindData(K | Flags) {
      assert(Loc.DebugInfoLoc.Line == 0);
   }

   PILLocation(Stmt *S, LocationKind K, unsigned Flags = 0)
      : Loc(S), KindData(K | Flags) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }

   PILLocation(Expr *E, LocationKind K, unsigned Flags = 0)
      : Loc(E), KindData(K | Flags) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }

   PILLocation(Decl *D, LocationKind K, unsigned Flags = 0)
      : Loc(D), KindData(K | Flags) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }

   PILLocation(Pattern *P, LocationKind K, unsigned Flags = 0)
      : Loc(P), KindData(K | Flags) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }

   PILLocation(SourceLoc L, LocationKind K, unsigned Flags = 0)
      : Loc(L), KindData(K | Flags) {
      setStorageKind(PILFileKind);
      assert(isPILFile());
   }

   PILLocation(DebugLoc L, LocationKind K, unsigned Flags = 0)
      : KindData(K | Flags) {
      setDebugInfoLoc(L);
   }
   /// @}

private:
   friend class ImplicitReturnLocation;
   friend class MandatoryInlinedLocation;
   friend class InlinedLocation;
   friend class CleanupLocation;

   void setLocationKind(LocationKind K) { KindData |= (K & LocationKindMask); }
   void setStorageKind(StorageKind K) { KindData |= (K & StorageKindMask); }
   unsigned getSpecialFlags() const { return KindData & SpecialFlagsMask; }
   void setSpecialFlags(unsigned Flags) {
      KindData |= (Flags & SpecialFlagsMask);
   }

   SourceLoc getSourceLoc(AstNodeTy N) const;
   SourceLoc getStartSourceLoc(AstNodeTy N) const;
   SourceLoc getEndSourceLoc(AstNodeTy N) const;

public:

   /// When an AstNode gets implicitly converted into a PILLocation we
   /// construct a RegularLocation. Since RegularLocations represent the majority
   /// of locations, this greatly simplifies the user code.
   PILLocation(Stmt *S) : Loc(S), KindData(RegularKind) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }
   PILLocation(Expr *E) : Loc(E), KindData(RegularKind) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }
   PILLocation(Decl *D) : Loc(D), KindData(RegularKind) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }
   PILLocation(Pattern *P) : Loc(P), KindData(RegularKind) {
      setStorageKind(AstNodeKind);
      assert(isAstNode());
   }

   /// Check if the location wraps an AST node or a valid PIL file
   /// location.
   ///
   /// Artificial locations and the top-level module locations will be null.
   bool isNull() const {
      switch (getStorageKind()) {
         case AstNodeKind:   return Loc.AstNode.Primary.isNull();
         case DebugInfoKind: return ExtraStorageData == 0;
         case PILFileKind:   return Loc.PILFileLoc.isInvalid();
         default:            return true;
      }
   }
   explicit operator bool() const { return !isNull(); }

   /// Return whether this location is backed by an AST node.
   bool isAstNode() const { return getStorageKind() == AstNodeKind; }

   /// Return whether this location came from a PIL file.
   bool isPILFile() const { return getStorageKind() == PILFileKind; }

   /// Return whether this location came from a textual PIL file.
   bool isDebugInfoLoc() const { return getStorageKind() == DebugInfoKind; }

   /// Marks the location as coming from auto-generated body.
   void markAutoGenerated() { KindData |= (1 << AutoGeneratedBit); }

   /// Returns true if the location represents an artificially generated
   /// body, such as thunks or default destructors.
   ///
   /// These locations should not be included in the debug line table.
   /// These might also need special handling by the debugger since they might
   /// contain calls, which the debugger could be able to step into.
   bool isAutoGenerated() const { return KindData & (1 << AutoGeneratedBit); }

   /// Returns true if the line number of this location is zero.
   bool isLineZero(const SourceManager &SM) const {
      return decodeDebugLoc(SM).Line == 0;
   }

   /// Changes the default source location position to point to start of
   /// the AST node.
   void pointToStart() { KindData |= (1 << PointsToStartBit); }

   /// Changes the default source location position to point to the end of
   /// the AST node.
   void pointToEnd() { KindData |= (1 << PointsToEndBit); }

   /// Mark this location as the location corresponding to the top-level
   /// (module-level) code.
   void markAsInTopLevel() { KindData |= (1 << IsInTopLevel); }

   /// Check is this location is associated with the top level/module.
   bool isInTopLevel() const { return KindData & (1 << IsInTopLevel); }

   /// Mark this location as being part of the function
   /// prologue, which means that it deals with setting up the stack
   /// frame. The first breakpoint location in a function is at the end
   /// of the prologue.
   void markAsPrologue() { KindData |= (1 << IsInPrologue); }

   /// Check is this location is part of a function's implicit prologue.
   bool isInPrologue() const { return KindData & (1 << IsInPrologue); }

   /// Add an AstNode to use as the location for debugging
   /// purposes if this location is different from the location used
   /// for diagnostics.
   template <typename T>
   void setDebugLoc(T *AstNodeForDebugging) {
      assert(!hasDebugLoc() && "DebugLoc already present");
      assert(isAstNode() && "not an AST location");
      Loc.AstNode.ForDebugger = AstNodeForDebugging;
   }
   bool hasDebugLoc() const {
      return isAstNode() && !Loc.AstNode.ForDebugger.isNull();
   }

   /// Populate this empty PILLocation with a DebugLoc.
   void setDebugInfoLoc(DebugLoc L) {
      ExtraStorageData = L.Filename.size();
      assert(ExtraStorageData == L.Filename.size() &&
             "file name is longer than 32 bits");
      Loc = L;
      setStorageKind(DebugInfoKind);
   }

   /// Check if the corresponding source code location definitely points
   ///  to the start of the AST node.
   bool alwaysPointsToStart() const { return KindData & (1 << PointsToStartBit);}

   /// Check if the corresponding source code location definitely points
   ///  to the end of the AST node.
   bool alwaysPointsToEnd() const { return KindData & (1 << PointsToEndBit); }

   LocationKind getKind() const {
      return LocationKind(KindData & LocationKindMask);
   }
   StorageKind getStorageKind() const {
      return StorageKind(KindData & StorageKindMask);
   }

   template <typename T>
   bool is() const {
      return T::isKind(*this);
   }

   template <typename T>
   T castTo() const {
      assert(T::isKind(*this));
      T t;
      PILLocation& tr = t;
      tr = *this;
      return t;
   }

   template <typename T>
   Optional<T> getAs() const {
      if (!T::isKind(*this))
         return Optional<T>();
      T t;
      PILLocation& tr = t;
      tr = *this;
      return t;
   }

   /// If the current value is of the specified AST unit type T,
   /// return it, otherwise return null.
   template <typename T> T *getAsAstNode() const {
      return isAstNode() ? getNodeAs<T>(Loc.AstNode.Primary) : nullptr;
   }

   /// Returns true if the Location currently points to the AST node
   /// matching type T.
   template <typename T> bool isAstNode() const {
      return isAstNode() && isNode<T>(Loc.AstNode.Primary);
   }

   /// Returns the primary value as the specified AST node type. If the
   /// specified type is incorrect, asserts.
   template <typename T> T *castToAstNode() const {
      assert(isAstNode());
      return castNodeTo<T>(Loc.AstNode.Primary);
   }

   /// If the DebugLoc is of the specified AST unit type T,
   /// return it, otherwise return null.
   template <typename T> T *getDebugLocAsAstNode() const {
      assert(hasDebugLoc() && "no debug location");
      return getNodeAs<T>(Loc.AstNode.ForDebugger);
   }

   /// Return the location as a DeclContext or null.
   DeclContext *getAsDeclContext() const;

   /// Convert a specialized location kind into a regular location.
   PILLocation getAsRegularLocation() {
      PILLocation RegularLoc = *this;
      RegularLoc.setLocationKind(RegularKind);
      return RegularLoc;
   }

   SourceLoc getDebugSourceLoc() const;
   SourceLoc getSourceLoc() const;
   SourceLoc getStartSourceLoc() const;
   SourceLoc getEndSourceLoc() const;
   SourceRange getSourceRange() const {
      return {getStartSourceLoc(), getEndSourceLoc()};
   }
   DebugLoc getDebugInfoLoc() const {
      assert(isDebugInfoLoc());
      return DebugLoc(Loc.DebugInfoLoc.Line, Loc.DebugInfoLoc.Column,
                      StringRef(Loc.DebugInfoLoc.FilenameData, ExtraStorageData));
   }

   /// Fingerprint a DebugLoc for use in a DenseMap.
   using DebugLocKey = std::pair<std::pair<unsigned, unsigned>, StringRef>;
   struct DebugLocHash : public DebugLocKey {
      DebugLocHash(DebugLoc L) : DebugLocKey({{L.Line, L.Column}, L.Filename}) {}
   };

   /// Extract the line, column, and filename.
   static DebugLoc decode(SourceLoc Loc, const SourceManager &SM);

   /// Return the decoded debug location.
   LLVM_NODISCARD DebugLoc decodeDebugLoc(const SourceManager &SM) const {
      return isDebugInfoLoc() ? getDebugInfoLoc()
                              : decode(getDebugSourceLoc(), SM);
   }

   /// Compiler-generated locations may be applied to instructions without any
   /// clear correspondence to an AST node in an otherwise normal function.
   static DebugLoc getCompilerGeneratedDebugLoc() {
      return {0, 0, "<compiler-generated>"};
   }

   /// Pretty-print the value.
   void dump(const SourceManager &SM) const;
   void print(raw_ostream &OS, const SourceManager &SM) const;

   /// Returns an opaque pointer value for the debug location that may
   /// be used to unique debug locations.
   const void *getOpaquePointerValue() const {
      if (isPILFile())
         return Loc.PILFileLoc.getOpaquePointerValue();
      if (isAstNode())
         return Loc.AstNode.Primary.getOpaqueValue();
      else
         return 0;
   }
   unsigned getOpaqueKind() const { return KindData; }

   inline bool operator==(const PILLocation& R) const {
      return KindData == R.KindData &&
             Loc.AstNode.Primary.getOpaqueValue() ==
             R.Loc.AstNode.Primary.getOpaqueValue() &&
             Loc.AstNode.ForDebugger.getOpaqueValue() ==
             R.Loc.AstNode.ForDebugger.getOpaqueValue();
   }
};

/// Allowed on any instruction.
class RegularLocation : public PILLocation {
public:
   RegularLocation(Stmt *S) : PILLocation(S, RegularKind) {}
   RegularLocation(Expr *E) : PILLocation(E, RegularKind) {}
   RegularLocation(Decl *D) : PILLocation(D, RegularKind) {}
   RegularLocation(Pattern *P) : PILLocation(P, RegularKind) {}
   RegularLocation(SourceLoc L) : PILLocation(L, RegularKind) {}
   RegularLocation(DebugLoc L) : PILLocation(L, RegularKind) {}

   /// Returns a location representing the module.
   static RegularLocation getModuleLocation() {
      RegularLocation Loc;
      Loc.markAsInTopLevel();
      return Loc;
   }

   /// If the current value is of the specified AST unit type T,
   /// return it, otherwise return null.
   template <typename T> T *getAs() const { return getNodeAs<T>(Loc.AstNode); }

   /// Returns true if the Location currently points to the AST node
   /// matching type T.
   template <typename T> bool is() const { return isNode<T>(Loc.AstNode); }

   /// Returns the primary value as the specified AST node type. If the
   /// specified type is incorrect, asserts.
   template <typename T> T *castTo() const { return castNodeTo<T>(Loc.AstNode); }

   /// Compiler-generated locations may be applied to instructions without any
   /// clear correspondence to an AST node in an otherwise normal function.
   /// The auto-generated bit also turns off certain diagnostics passes such.
   static RegularLocation getAutoGeneratedLocation() {
      RegularLocation AL(getCompilerGeneratedDebugLoc());
      AL.markAutoGenerated();
      return AL;
   }

   /// Returns a location that is compiler-generated, but with a hint as to where
   /// it may have been generated from. These locations will have an artificial
   /// line location of zero in DWARF, but in CodeView we want to use the given
   /// line since line zero does not represent an artificial line in CodeView.
   static RegularLocation getAutoGeneratedLocation(SourceLoc L) {
      RegularLocation AL(L);
      AL.markAutoGenerated();
      return AL;
   }

private:
   RegularLocation() : PILLocation(RegularKind) {}

   friend class PILLocation;
   static bool isKind(const PILLocation& L) {
      return L.getKind() == RegularKind;
   }
};

/// Used to represent a return instruction in user code.
///
/// Allowed on an BranchInst, ReturnInst.
class ReturnLocation : public PILLocation {
public:
   ReturnLocation(ReturnStmt *RS);

   /// Construct the return location for a constructor or a destructor.
   ReturnLocation(BraceStmt *BS);

   ReturnStmt *get();

private:
   friend class PILLocation;
   static bool isKind(const PILLocation& L) {
      return L.getKind() == ReturnKind;
   }
};

/// Used on the instruction that was generated to represent an implicit
/// return from a function.
///
/// Allowed on an BranchInst, ReturnInst.
class ImplicitReturnLocation : public PILLocation {
public:

   ImplicitReturnLocation(AbstractClosureExpr *E);

   ImplicitReturnLocation(ReturnStmt *S);

   ImplicitReturnLocation(AbstractFunctionDecl *AFD);

   /// Construct from a RegularLocation; preserve all special bits.
   ///
   /// Note, this can construct an implicit return for an arbitrary expression
   /// (specifically, in case of auto-generated bodies).
   static PILLocation getImplicitReturnLoc(PILLocation L);

   AbstractClosureExpr *get();

private:
   friend class PILLocation;
   static bool isKind(const PILLocation& L) {
      return L.getKind() == ImplicitReturnKind;
   }
   ImplicitReturnLocation() : PILLocation(ImplicitReturnKind) {}
};

/// Marks instructions that correspond to inlined function body and
/// setup code. This location should not be used for inlined transparent
/// bodies, see MandatoryInlinedLocation.
///
/// This location wraps the call site AstNode.
///
/// Allowed on any instruction except for ReturnInst.
class InlinedLocation : public PILLocation {
public:
   InlinedLocation(Expr *CallSite) : PILLocation(CallSite, InlinedKind) {}
   InlinedLocation(Stmt *S) : PILLocation(S, InlinedKind) {}
   InlinedLocation(Pattern *P) : PILLocation(P, InlinedKind) {}
   InlinedLocation(Decl *D) : PILLocation(D, InlinedKind) {}

   /// Constructs an inlined location when the call site is represented by a
   /// PILFile location.
   InlinedLocation(SourceLoc L) : PILLocation(InlinedKind) {
      setStorageKind(PILFileKind);
      Loc.PILFileLoc = L;
   }

   static InlinedLocation getInlinedLocation(PILLocation L);

private:
   friend class PILLocation;
   static bool isKind(const PILLocation& L) {
      return L.getKind() == InlinedKind;
   }
   InlinedLocation() : PILLocation(InlinedKind) {}

   InlinedLocation(Expr *E, unsigned F) : PILLocation(E, InlinedKind, F) {}
   InlinedLocation(Stmt *S, unsigned F) : PILLocation(S, InlinedKind, F) {}
   InlinedLocation(Pattern *P, unsigned F) : PILLocation(P, InlinedKind, F) {}
   InlinedLocation(Decl *D, unsigned F) : PILLocation(D, InlinedKind, F) {}
   InlinedLocation(SourceLoc L, unsigned F) : PILLocation(L, InlinedKind, F) {}

   static InlinedLocation getModuleLocation(unsigned Flags) {
      auto L = InlinedLocation();
      L.setSpecialFlags(Flags);
      return L;
   }

};

/// Marks instructions that correspond to inlined function body and
/// setup code for transparent functions, inlined as part of mandatory inlining
/// pass.
///
/// This location wraps the call site AstNode.
///
/// Allowed on any instruction except for ReturnInst.
class MandatoryInlinedLocation : public PILLocation {
public:
   MandatoryInlinedLocation(Expr *CallSite) :
      PILLocation(CallSite, MandatoryInlinedKind) {}
   MandatoryInlinedLocation(Stmt *S) : PILLocation(S, MandatoryInlinedKind) {}
   MandatoryInlinedLocation(Pattern *P) : PILLocation(P, MandatoryInlinedKind) {}
   MandatoryInlinedLocation(Decl *D) : PILLocation(D, MandatoryInlinedKind) {}

   /// Constructs an inlined location when the call site is represented by a
   /// PILFile location.
   MandatoryInlinedLocation(SourceLoc L)
      : PILLocation(L, MandatoryInlinedKind) {}

   static MandatoryInlinedLocation getMandatoryInlinedLocation(PILLocation L);
   static MandatoryInlinedLocation getAutoGeneratedLocation();

   static MandatoryInlinedLocation getModuleLocation(unsigned Flags) {
      auto L = MandatoryInlinedLocation();
      L.setSpecialFlags(Flags);
      return L;
   }

private:
   friend class PILLocation;
   static bool isKind(const PILLocation& L) {
      return L.getKind() == MandatoryInlinedKind;
   }
   MandatoryInlinedLocation() : PILLocation(MandatoryInlinedKind) {}
   MandatoryInlinedLocation(Expr *E, unsigned F)
      : PILLocation(E, MandatoryInlinedKind, F) {}
   MandatoryInlinedLocation(Stmt *S, unsigned F)
      : PILLocation(S, MandatoryInlinedKind, F) {}
   MandatoryInlinedLocation(Pattern *P, unsigned F)
      : PILLocation(P, MandatoryInlinedKind, F) {}
   MandatoryInlinedLocation(Decl *D, unsigned F)
      : PILLocation(D, MandatoryInlinedKind, F) {}
   MandatoryInlinedLocation(SourceLoc L, unsigned F)
      : PILLocation(L, MandatoryInlinedKind, F) {}
   MandatoryInlinedLocation(DebugLoc L, unsigned F)
      : PILLocation(L, MandatoryInlinedKind, F) {}
};

/// Used on the instruction performing auto-generated cleanup such as
/// deallocs, destructor calls.
///
/// The cleanups are performed after completing the evaluation of the AST Node
/// wrapped inside the PILLocation. This location wraps the statement
/// representing the enclosing scope, for example, FuncDecl, ParenExpr. The
/// scope's end location points to the SourceLoc that shows when the operation
/// is performed at runtime.
///
/// Allowed on any instruction except for ReturnInst.
/// Locations of an inlined destructor should also be represented by this.
class CleanupLocation : public PILLocation {
public:
   CleanupLocation(Expr *E) : PILLocation(E, CleanupKind) {}
   CleanupLocation(Stmt *S) : PILLocation(S, CleanupKind) {}
   CleanupLocation(Pattern *P) : PILLocation(P, CleanupKind) {}
   CleanupLocation(Decl *D) : PILLocation(D, CleanupKind) {}

   static CleanupLocation get(PILLocation L);

   /// Returns a location representing a cleanup on the module level.
   static CleanupLocation getModuleCleanupLocation() {
      CleanupLocation Loc;
      Loc.markAsInTopLevel();
      return Loc;
   }

private:
   friend class PILLocation;
   static bool isKind(const PILLocation& L) {
      return L.getKind() == CleanupKind;
   }
   CleanupLocation() : PILLocation(CleanupKind) {}

   CleanupLocation(Expr *E, unsigned F) : PILLocation(E, CleanupKind, F) {}
   CleanupLocation(Stmt *S, unsigned F) : PILLocation(S, CleanupKind, F) {}
   CleanupLocation(Pattern *P, unsigned F) : PILLocation(P, CleanupKind, F) {}
   CleanupLocation(Decl *D, unsigned F) : PILLocation(D, CleanupKind, F) {}
};

/// Used to represent an unreachable location that was
/// auto-generated and has no correspondence to user code. It should
/// not be used in diagnostics or for debugging.
///
/// Differentiates an unreachable instruction, which is generated by
/// DCE, from an unreachable instruction in user code (output of PILGen).
/// Allowed on an unreachable instruction.
class ArtificialUnreachableLocation : public PILLocation {
public:
   ArtificialUnreachableLocation() : PILLocation(ArtificialUnreachableKind) {}
private:
   friend class PILLocation;
   static bool isKind(const PILLocation& L) {
      return (L.getKind() == ArtificialUnreachableKind);
   }
};

class PILDebugScope;

/// A PILLocation paired with a PILDebugScope.
class PILDebugLocation {
   const PILDebugScope *Scope = nullptr;
   PILLocation Location;

public:
   PILDebugLocation()
      : Scope(nullptr),
        Location(RegularLocation::getAutoGeneratedLocation()) {}
   PILDebugLocation(PILLocation Loc, const PILDebugScope *DS)
      : Scope(DS), Location(Loc) {}
   PILLocation getLocation() const { return Location; }
   const PILDebugScope *getScope() const { return Scope; }
};

} // end polar namespace

#endif
